package net.ziqiang.sdk.teagreen.util;

import java.util.*;
import java.io.*;

public class Properties extends java.util.Properties{
	private static final long serialVersionUID = 8775044918929445212L;

	/**
     * Load a properties file with given path and charsetName.
     * @param path
     * @param charsetName
     * @throws IOException
     */
    public synchronized void load(String path,String charsetName) throws IOException{
    	File file=new File(path);
    	FileInputStream fis=new FileInputStream(file);
    	load(fis,charsetName);
    	fis.close();
    }
    
    /**
     * Load properties from a given FileInputStream and charsetName.
     * @param fileInputStream
     * @param charsetName
     * @throws IOException
     */
    private synchronized void load(FileInputStream fileInputStream, String charsetName) throws IOException{
    	InputStreamReader isr=new InputStreamReader(fileInputStream,charsetName);
    	load(isr);
    	isr.close();
    }

	/**
	 * @author Tan Menglong <a href="mailto:tanmenglong@gmail.com">TanMenglong@gmail.com</a>
	 * @param out
	 * @param comments
	 * @param charsetName
	 * @throws IOException
	 */
	public void store(OutputStream out, String comments, String charsetName) throws IOException{
		OutputStreamWriter osw=new OutputStreamWriter(out, charsetName);
		BufferedWriter bf=new BufferedWriter(osw);
	    store0(bf,comments,true);
	    
	    osw.close();
	    bf.close();
	}
	
	/**
	 * Save properties file to a given path with charset.
	 * @param path
	 * @param comments
	 * @param charsetName
	 * @throws IOException
	 */
	public void store(String path, String comments, String charsetName) throws IOException{
		File file=new File(path);
		if(!file.exists())
			file.createNewFile();
		FileOutputStream fos=new FileOutputStream(file);
		OutputStreamWriter osw=new OutputStreamWriter(fos, charsetName);
		BufferedWriter bf=new BufferedWriter(osw);
		store0(bf,comments,true);
		
		osw.close();
		fos.close();
		bf.close();
	}
    
	/**
	 * Save properties to a given File with charset.
	 * @param file
	 * @param comments
	 * @param charsetName
	 * @throws IOException
	 */
	public void store(File file, String comments, String charsetName) throws IOException{
		FileOutputStream fos=new FileOutputStream(file);
		OutputStreamWriter osw=new OutputStreamWriter(fos, charsetName);
		BufferedWriter bf=new BufferedWriter(osw);
		store0(bf,comments,true);
		
		osw.close();
		fos.close();
		bf.close();
	}

	@SuppressWarnings("unchecked")
	private void store0(BufferedWriter bw, String comments, boolean escUnicode) throws IOException{
	    if (comments != null) {
	        writeComments(bw, comments);
	    }
	    bw.write("#" + new Date().toString());
	    bw.newLine();
		synchronized (this) {
		        for (Enumeration e = keys(); e.hasMoreElements();) {
		            String key = (String)e.nextElement();
			String val = (String)get(key);
			key = saveConvert(key, true, escUnicode);
			/* No need to escape embedded and trailing spaces for value, hence
			 * pass false to flag.
			 */
			val = saveConvert(val, false, escUnicode);
			bw.write(key + "=" + val);
		            bw.newLine();
		    }
		}
	    bw.flush();
	}

    private static void writeComments(BufferedWriter bw, String comments) 
        throws IOException {
        bw.write("#");
        int len = comments.length();  
        int current = 0;
        int last = 0;
        char[] uu = new char[6];
        uu[0] = '\\';
        uu[1] = 'u';
        while (current < len) {
            char c = comments.charAt(current);
	    if (c > '\u00ff' || c == '\n' || c == '\r') {
	        if (last != current) 
                    bw.write(comments.substring(last, current));
                if (c > '\u00ff') {
                    uu[2] = toHex((c >> 12) & 0xf);
                    uu[3] = toHex((c >>  8) & 0xf);
                    uu[4] = toHex((c >>  4) & 0xf);
                    uu[5] = toHex( c        & 0xf);
                    bw.write(new String(uu));
                } else {
                    bw.newLine();
                    if (c == '\r' && 
			current != len - 1 && 
			comments.charAt(current + 1) == '\n') {
                        current++;
                    }
                    if (current == len - 1 ||
                        (comments.charAt(current + 1) != '#' &&
			comments.charAt(current + 1) != '!'))
                        bw.write("#");
                }
                last = current + 1;
	    } 
            current++;
	}
        if (last != current) 
            bw.write(comments.substring(last, current));
        bw.newLine();
    }

    /*
     * Converts unicodes to encoded &#92;uxxxx and escapes
     * special characters with a preceding slash
     */
    private String saveConvert(String theString,
			       boolean escapeSpace,
			       boolean escapeUnicode) {
        int len = theString.length();
        int bufLen = len * 2;
        if (bufLen < 0) {
            bufLen = Integer.MAX_VALUE;
        }
        StringBuffer outBuffer = new StringBuffer(bufLen);

        for(int x=0; x<len; x++) {
            char aChar = theString.charAt(x);
            // Handle common case first, selecting largest block that
            // avoids the specials below
            if ((aChar > 61) && (aChar < 127)) {
                if (aChar == '\\') {
                    outBuffer.append('\\'); outBuffer.append('\\');
                    continue;
                }
                outBuffer.append(aChar);
                continue;
            }
            switch(aChar) {
		case ' ':
		    if (x == 0 || escapeSpace) 
			outBuffer.append('\\');
		    outBuffer.append(' ');
		    break;
                case '\t':outBuffer.append('\\'); outBuffer.append('t');
                          break;
                case '\n':outBuffer.append('\\'); outBuffer.append('n');
                          break;
                case '\r':outBuffer.append('\\'); outBuffer.append('r');
                          break;
                case '\f':outBuffer.append('\\'); outBuffer.append('f');
                          break;
                case '=': // Fall through
                case ':': // Fall through
                case '#': // Fall through
                case '!':
                    outBuffer.append('\\'); outBuffer.append(aChar);
                    break;
                default:
                    if (((aChar < 0x0020) || (aChar > 0x007e)) & escapeUnicode ) {
                        outBuffer.append('\\');
                        outBuffer.append('u');
                        outBuffer.append(toHex((aChar >> 12) & 0xF));
                        outBuffer.append(toHex((aChar >>  8) & 0xF));
                        outBuffer.append(toHex((aChar >>  4) & 0xF));
                        outBuffer.append(toHex( aChar        & 0xF));
                    } else {
                        outBuffer.append(aChar);
                    }
            }
        }
        return outBuffer.toString();
    }

    /**
     * Convert a nibble to a hex character
     * @param	nibble	the nibble to convert.
     */
    private static char toHex(int nibble) {
    	return hexDigit[(nibble & 0xF)];
    }

    /** A table of hex digits */
    private static final char[] hexDigit = {
	'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
    };}
